from sqlalchemy.orm import Session
from sqlalchemy import func
from typing import List
import datetime
import time
import models
import schemas
import trans_time
from databases import SessionLocal, engine
import pandas as pd

# 获取数据库
db = SessionLocal()

'''
本文件负责定义处理数据库的函数
'''


def status(db: Session, line: str, network: bool, PLC: bool, db_interval: int, startby: int):
    '''
    本函数用于记录终端的状态，并存入数据库待查
    :param db: 数据库（表名LineStatus）
    :param line: 哪条线的名称
    :param network: 网络状态，能上传都是True
    :param PLC: PLC的网络状态，能Ping通就是True
    :param db_interval: 终端数据库最后一条数据与现在的时间间隔，单位是秒
    :param startby: 数据的上传起点（一般等于最后一次上传时数据库的总条数）
    :return:
    '''
    line_status_data = models.LineStatus(Line=line,
                                         Network=network,
                                         PLC=PLC,
                                         DB_interval=db_interval,
                                         Startby=startby)
    db.add(line_status_data)
    db.commit()


def get_status(db: Session, line: str):
    '''
    本函数用于获取某条线上次开始上传数据的起点，防止终端ini文件丢失造成的故障
    :param db: 数据库（表名LineStatus）
    :param line: 哪条线的名称
    :return: 返回起点的数值
    '''
    result = db.query(models.LineStatus).filter(models.LineStatus.Line == line).order_by(
        -models.LineStatus.UpdateTime).first()
    return result.Startby, result.PLC, result.DB_interval, result.UpdateTime


def storage(db: Session, data: List[schemas.Upload]):
    '''
    本函数用于存储终端上传的短暂停和电眼数据
    :param db: 数据库（表名Pause）
    :param data: 一个数据的列表
    :return:
    '''
    data_list = []
    try:
        for index_d in data:
            d = models.Pause(**index_d.dict())
            data_list.append(d)
        db.add_all(data_list)
        db.commit()
        # db.refresh(models.Pause)
        return True
    except Exception as e:
        print(e)
        return False


def analyse(db: Session, interval: int):
    '''
    本函数用于匹配“急停恢复”与“急停按下”一一匹配的。先查找第一个“急停恢复”的数据，然后查找跟他一样属性的“急停按下”数据
    并计算他们的时间差。
    完成匹配后，“急停恢复”的数据isMatch则设置为True，以后不再匹配
    :param db: 数据库（表名Pause）
    :param interval: 每次匹配的数量
    :return:
    '''
    # 定义一个待提交数据的空列表
    up_data = []
    start_time = datetime.datetime.now()
    for index in range(interval):
        # 获得最后一条AlarmType是“急停恢复”的数据,且没有匹配过（isMatch不是True）
        end_row = db.query(models.Pause).filter(models.Pause.isMatch == None,
                                                models.Pause.AlarmType.like("%恢复%")).order_by(
            -models.Pause.DateTime).first()
        if end_row:
            # 获得一条AlarmType是“急停按下”的数据数据，要求跟last_row同一条生产线，而且没有匹配过（isMatch不是True）
            start_row = db.query(models.Pause).filter(models.Pause.line == end_row.line,
                                                      models.Pause.isMatch == None,
                                                      models.Pause.DateTime <= end_row.DateTime,
                                                      models.Pause.VarName == end_row.VarName,
                                                      models.Pause.AlarmType.like("%按下%")).order_by(
                -models.Pause.DateTime).first()
            if start_row:
                end_row.isMatch = True
                # 形成一个Analyse_Pause实例
                analyse_data = models.Analyse_Pause(
                    StartLine=start_row.line,
                    StartVarName=start_row.VarName,
                    StartAlarm=start_row.AlarmType,
                    StartID=start_row.ID,
                    StartTime=start_row.DateTime,
                    EndLine=end_row.line,
                    EndVarName=end_row.VarName,
                    EndAlarm=end_row.AlarmType,
                    EndID=end_row.ID,
                    EndTime=end_row.DateTime,
                    Interval=(end_row.DateTime - start_row.DateTime).seconds,
                    isActive=True if (
                                         end_row.DateTime - start_row.DateTime).seconds < 300 else False
                )
                up_data.append(analyse_data)
                # 提交更新end_row.isMatch为True
                db.commit()
            else:
                # 一个都匹配不上，这个急停恢复数据是孤立的，将他的isMatch为True
                end_row.isMatch = True
                # 提交更新end_row.isMatch为True
                db.commit()
        else:
            # 已经到尽头了，所有数据都匹配过了
            print('已经找不到数据了')
            break
    # 提交所有匹配上的数据
    db.add_all(up_data)
    db.commit()
    end_time = datetime.datetime.now()
    return '已完成%s对数据匹配,用时%s秒' % (len(up_data), (end_time - start_time).seconds)


def getdata(db: Session, start: str, end: str, line: str = None):
    '''
    本函数用于获取某条线某个时间段内的急停数据（已经分析过的）
    :param db: 数据库（表名Analyse_Pause）
    :param start: 开始时间，格式2020-4-26 13:00:00
    :param end: 结束时间，格式同上
    :param line: 哪条线，若为None，则返回全部线
    :return:
    '''
    data = {}
    txt = {}
    myline = line
    # 如果没有指定哪条线，则每条线循环获取前三的急停影响时间
    if myline is None:
        from main import LineName
        # 取得每条线的短暂停数据，按急停点名称分组，计算按下数量、急停时间汇总，并倒序排列
        for line in LineName:
            result = db.query(models.Analyse_Pause.EndVarName, func.count(models.Analyse_Pause.StartID),
                              func.sum(models.Analyse_Pause.Interval)).group_by(models.Analyse_Pause.EndVarName).filter(
                models.Analyse_Pause.EndLine == line,
                models.Analyse_Pause.StartTime.between(start, end),
                models.Analyse_Pause.isActive == True).order_by(-func.sum(models.Analyse_Pause.Interval)).all()
            # 如果有数据的
            if result:
                # 获得总的急停时间
                sum_time = 0
                for i in result:
                    sum_time = sum_time + i[2]
                # 总的急停时间少于120秒，相当优秀
                if sum_time <= 120:
                    text = '{0}-{1}，{2}线就是那么优秀，急停影响时间累积{3}秒，继续保持，好样的~~~'.format(start[10:-3],
                                                                                end[10:-3], line, sum_time)
                # 超过120秒，则分三种情况进行播报
                else:
                    if len(result) >= 3:
                        text = '{0}-{1}，{2}线急停次数前三位是：\n{3}：\n按下{4}次，影响时间合计{5}秒\n{6}：\n' \
                               '按下{7}次，影响时间合计{8}秒\n{9}：\n按下{10}次，影响时间合计{11}秒'.format(
                            start[10:-3], end[10:-
                            3], line, result[0][0], result[0][1], result[0][2],
                            result[1][0], result[1][1], result[1][2],
                            result[2][0], result[2][1], result[2][2]
                        )
                        print(text)
                    elif len(result) == 2:
                        text = '{0}-{1}，{2}线急停次数前两位是：\n{3}：\n按下{4}次，影响时间合计{5}秒\n{6}：\n' \
                               '按下{7}次，影响时间合计{8}秒'.format(
                            start[10:-3], end[10:-
                            3], line, result[0][0], result[0][1], result[0][2],
                            result[1][0], result[1][1], result[1][2])
                        print(text)
                    elif len(result) == 1:
                        text = '{0}-{1}，{2}线急停只有{3}：\n但时间比较长，其按下{4}次，影响时间合计{5}秒\n'.format(
                            start[10:-3], end[10:-3], line, result[0][0], result[0][1], result[0][2])
                        print(text)
                data.update({line: result})
                txt.update({line: text})
            else:
                text = '{0}线没有急停数据，请确认终端是否正确上传数据。。。'.format(line)
                print(text)
                txt.update({line: text})
        return data, txt
    else:
        result = db.query(models.Analyse_Pause.EndVarName, func.count(models.Analyse_Pause.StartID),
                          func.sum(models.Analyse_Pause.Interval)).group_by(models.Analyse_Pause.EndVarName).filter(
            models.Analyse_Pause.EndLine == myline,
            models.Analyse_Pause.StartTime.between(start, end),
            models.Analyse_Pause.isActive == True).order_by(-func.sum(models.Analyse_Pause.Interval)).all()
        txt = ""
        return result, txt


def add_user(db: Session, user_ID: int, factory: str, name: str, pinyin: str, email: str, tel: str, isActive: bool):
    user = models.Users(
        User_ID=user_ID,
        Factory=factory,
        Name=name,
        Pinyin=pinyin,
        Email=email,
        Tel=tel,
        isActive=isActive
    )
    db.add(user)
    db.commit()


def get_notice_num(db: Session, user_id: int, title: str):
    # 根据user_ID获取这个用户的数据
    user = db.query(models.Users).filter(
        models.Users.User_ID == user_id).first()
    # 获得用户的名字和工厂
    name = user.Name
    factory = user.Factory
    # 获得工厂最后一条数据
    last = db.query(models.Notice).filter(models.Notice.Factory ==
                                          factory).order_by(-models.Notice.UpdateTime).first()
    if last:
        # 编号格式[2020]15号
        lastnum = last.Notice_num
    else:
        lastnum = "[2020]0号"
    # 查找有没有重复的公告名称
    old = db.query(models.Notice).filter(models.Notice.Name_ID ==
                                         user_id, models.Notice.Title == title).first()
    if old:
        return '你的公告"%s"重复了,请确认是否编辑错误！' % title
    else:
        num = lastnum.replace(lastnum[6:-1], str(int(lastnum[6:-1]) + 1))
        notice = models.Notice(
            Factory=factory,
            Notice_num=num,
            Name=name,
            Title=title,
            Name_ID=user_id
        )
        db.add(notice)
        db.commit()
        return '你的公告"%s",编号是%s' % (title, num)


def get_contract(db: Session, user_id: int, title: str, company: str, price: int):
    # 根据user_ID获取这个用户的数据
    user = db.query(models.Users).filter(
        models.Users.User_ID == user_id).first()
    # 获得用户的名字和工厂
    name, = user.Name
    factory = user.Factory
    # 获得工厂最后一条数据
    last = db.query(models.Contract).filter(models.Contract.Factory == factory).order_by(
        -models.Contract.UpdateTime).first()
    if last:
        # 编号格式HXGDKT-ZB-2020017
        lastnum = last.Contract_num
    else:
        lastnum = "HXGDKT-ZB-2020000"
    # 查找有没有重复的合同名称
    old = db.query(models.Contract).filter(
        models.Contract.Name_ID == user_id, models.Contract.Title == title).first()
    if old:
        return '你的合同"%s"发现有重复,请确认是否编辑错误！' % title
    else:
        year = datetime.datetime.today().year
        num = lastnum.replace(lastnum[10:], str(
            year) + str(int(lastnum[-3:]) + 1))
        contract = models.Contract(
            Factory=factory,
            Contract_num=num,
            Title=title,
            Name=name,
            Name_ID=user_id,
            Company=company,
            Price=price
        )
        db.add(contract)
        db.commit()
        return contract


def get_company_info(db: Session, company: str):
    info = db.query(models.Company).filter(
        models.Company.Company.like("%{0}%".format(company))).all()
    return info


def get_user_info(db: Session, user_id: int):
    # 根据user_ID获取这个用户的数据
    user = db.query(models.Users).filter(
        models.Users.User_ID == user_id).first()
    # 获得用户的名字和工厂
    name = user.Name
    factory = user.Factory
    return name, factory


# 以下是生产看板的对应函数

def get_time():
    '''
    获得服务器的时间
    :return:
    '''
    time_str = time.strftime("%Y{}%m{}%d{} %X")
    return time_str.format("年", "月", "日")


def get_pause_data(db: Session, line: str, interval=20, workday=False):
    '''
    获得急停点数据，时间间隔是20分钟
    :param db: 数据库
    :param interval: 时间间隔，单位分钟
    :param workday: 是否对当班（8:00开始或20:00开始）进行统计
    :return: 返回查询出来的急停点数据集合
    '''
    if workday:
        end_time = datetime.datetime.now()
        # 20点及以后，选当天的20点作为开始时间
        if end_time.hour >= 20:
            start_time = datetime.datetime(
                end_time.year, end_time.month, end_time.day, 20, 0, 0)
        # 8点前，选前一天的20点作为开始时间
        elif end_time.hour < 8:
            start_time = datetime.datetime(
                end_time.year, end_time.month, end_time.day - 1, 20, 0, 0)
        else:
            start_time = datetime.datetime(
                end_time.year, end_time.month, end_time.day, 8, 0, 0)
    else:
        end_time = datetime.datetime.now()
        start_time = end_time - datetime.timedelta(minutes=interval)
    start = start_time.strftime("%Y-%m-%d %H:%M:%S")
    end = end_time.strftime("%Y-%m-%d %H:%M:%S")
    # start = "2020-5-11 8:00:00"
    # end = "2020-5-11 20:00:00"
    result = db.query(models.Analyse_Pause.EndVarName, func.count(models.Analyse_Pause.StartID),
                      func.sum(models.Analyse_Pause.Interval)).group_by(models.Analyse_Pause.EndVarName).filter(
        models.Analyse_Pause.EndLine == line,
        models.Analyse_Pause.StartTime.between(start, end),
        models.Analyse_Pause.isActive == True).order_by(-func.sum(models.Analyse_Pause.Interval)).all()
    return result


def get_count(db: Session, line: str, point: str, interval=20, workday=False, start_arg=None):
    '''
    获得某个监控点的某个时间间隔的数量
    :param db: 数据库
    :param line: 线体名称
    :param point: 监控点的名称
    :param interval: 时间间隔，默认20分钟
    :param workday: 是否对当班（8:00开始或20:00开始）进行统计
    :return: 返回监控点的数量
    '''
    if workday:
        end_time = datetime.datetime.now()
        # 20点及以后，选当天的20点作为开始时间
        if end_time.hour >= 20:
            start_time = datetime.datetime(
                end_time.year, end_time.month, end_time.day, 20, 0, 0)
        # 8点前，选前一天的20点作为开始时间
        elif end_time.hour < 8:
            start_time = datetime.datetime(
                end_time.year, end_time.month, end_time.day - 1, 20, 0, 0)
        else:
            start_time = datetime.datetime(
                end_time.year, end_time.month, end_time.day, 8, 0, 0)
    else:
        if start_arg is None:
            end_time = datetime.datetime.now()
            start_time = end_time - datetime.timedelta(minutes=interval)
        else:
            start = str(datetime.datetime.today()).split()[0] + " " + start_arg
            start_time = trans_time.string_toDatetime(start)
            end_time = start_time + datetime.timedelta(minutes=interval)
    point = "%" + point + "%"
    start = start_time.strftime("%Y-%m-%d %H:%M:%S")
    end = end_time.strftime("%Y-%m-%d %H:%M:%S")
    num = db.query(func.count(models.Pause.VarName)).filter(models.Pause.line == line,
                                                            models.Pause.VarName.like(
                                                                point),
                                                            models.Pause.DateTime.between(start, end)).first()
    return num[0]


def get_line_beat(db: Session, line: str, keyword="节拍", interval=20, workday=False):
    '''
    获得线体节拍的数据
    :param db: 数据库
    :param line: 线体名称
    :param interval: 计算节拍的时间间隔，默认20秒
    :param workday: 是否对当班（8:00开始或20:00开始）进行统计
    :return: 返回一个字典，数据点（名字包含“节拍”2字）及其平均节拍
    '''
    if workday:
        end_time = datetime.datetime.now()
        # 20点及以后，选当天的20点作为开始时间
        if end_time.hour >= 20:
            start_time = datetime.datetime(
                end_time.year, end_time.month, end_time.day, 20, 0, 0)
        # 8点前，选前一天的20点作为开始时间
        elif end_time.hour < 8:
            start_time = datetime.datetime(
                end_time.year, end_time.month, end_time.day - 1, 20, 0, 0)
        else:
            start_time = datetime.datetime(
                end_time.year, end_time.month, end_time.day, 8, 0, 0)
    else:
        end_time = datetime.datetime.now()
        start_time = end_time - datetime.timedelta(minutes=interval)
    start = start_time.strftime("%Y-%m-%d %H:%M:%S")
    end = end_time.strftime("%Y-%m-%d %H:%M:%S")
    # 读取数据源
    sql = "select * from pause where DateTime between '{0}' AND '{1}' AND Line Like '{2}';".format(
        start, end, line)
    df = pd.read_sql_query(sql, engine)
    # 筛选出有“节拍”有关的数据
    newdata = df.loc[(df["VarName"].str.contains(keyword)), ["VarName", "AlarmType", "DateTime"]].sort_values(
        by="DateTime")
    newdata["VarName"] = newdata["VarName"].str.replace("光电", "")
    # 分组
    grouped = newdata.groupby(by="VarName")
    all_pd = pd.DataFrame({})
    # 遍历分组
    for group in grouped.groups:
        pd_1 = grouped.get_group(group).sort_values(by="DateTime")
        # DateTime列每个数据减去前一个数据
        pd_2 = pd_1["DateTime"].diff().dt.seconds
        # 筛选出结果大于0的节拍
        npd = pd_2.loc[pd_2 > 0]
        # 重置索引后存入空DateTime
        all_pd[group] = npd.reset_index(drop=True)
    print(all_pd)
    data_pd = all_pd.describe().loc['mean'].round(1)
    print(data_pd)
    return data_pd.to_dict()


def get_longtime(line: str, keyword="产量", interval=120):
    import pandas as pd
    end_time = datetime.datetime.now()
    # 20点及以后，选当天的20点作为开始时间
    if end_time.hour >= 20:
        start_time = datetime.datetime(
            end_time.year, end_time.month, end_time.day, 20, 0, 0)
    # 8点前，选前一天的20点作为开始时间
    elif end_time.hour < 8:
        start_time = datetime.datetime(
            end_time.year, end_time.month, end_time.day - 1, 20, 0, 0)
    else:
        start_time = datetime.datetime(
            end_time.year, end_time.month, end_time.day, 8, 0, 0)
    start = start_time.strftime("%Y-%m-%d %H:%M:%S")
    end = end_time.strftime("%Y-%m-%d %H:%M:%S")
    # 读取数据源
    sql = "select * from pause where DateTime between '{0}' AND '{1}' AND Line Like '{2}';".format(
        start, end, line)
    df = pd.read_sql_query(sql, engine)
    print(df)
    # 筛选出有“产量”有关的数据
    newdata = df.loc[(df["VarName"].str.contains("产量")), ["VarName", "AlarmType", "DateTime"]].sort_values(
        by="DateTime")
    newdata.set_index("DateTime", inplace=True, drop=False)
    pd1 = newdata["DateTime"].diff().dt.seconds
    # 筛选出大于120秒的
    pd2 = pd1.loc[pd1 > interval]
    print(pd2.sum() - 7200)
    if end_time.hour >= 20:
        return int(pd2.sum())
    elif end_time.hour < 8:
        return int((pd2.sum() - 7200))
    elif end_time.hour >= 8 and end_time.hour < 12:
        return int(pd2.sum())
    elif end_time.hour >= 12 and end_time.hour < 5:
        return int(pd2.sum() - 3600)
    elif end_time.hour >= 5:
        return int(pd2.sum() - 7200)

    return int((pd2.sum() - 7200) / 60)


def get_beat(df, keyword="节拍", replace_words=["光电", "环形", "急停"], method='mean', thresholds=0,
             cols=["VarName", "AlarmType", "DateTime"]):
    '''
    5月27日重构的最新版函数
    用于获取关键字(keyword)的数据点(VarName)，在一个时间段内的平均节拍（method='mean'）或累计时间（method="sum"）
    还设定了阈值(thresholds)，只有大于阈值的时间间隔才计算
    '''
    # 筛选出有关键字keyword(“节拍”)有关的数据
    df_keyword = df.loc[(df[cols[0]].str.contains(keyword)), cols].sort_values(
        by=cols[-1])
    # 替换掉多余的文字
    for word in replace_words:
        df_keyword[cols[0]] = df_keyword[cols[0]].str.replace(word, "")
    # 根据名称VarName分组
    grouped = df_keyword.groupby(by=cols[0])
    # 定义一个空的dataframe
    all_pd = pd.DataFrame({})
    # 遍历分组
    for group in grouped.groups:
        pd_1 = grouped.get_group(group).sort_values(by=cols[-1])
        # DateTime列每个数据减去前一个数据
        pd_2 = pd_1[cols[-1]].diff().dt.seconds
        print(pd_2)
        # 筛选出结果大于阈值(0秒)的节拍（可能采集原因，可能存在结果是0的数据）
        npd = pd_2.loc[pd_2 > thresholds]
        # 筛选出结果少于900秒的节拍（由于吃饭原因，将大于15分钟的数据也剔除）
        npd = npd.loc[npd < 900]
        # 重置索引后存入空DateTime
        all_pd[group] = npd.reset_index(drop=True)
    # print(all_pd)
    if method == "mean":
        # 取出统计平均数，并保留1位小数
        data_pd = all_pd.describe().loc['mean'].round(1)
    elif method == "sum":
        data_pd = all_pd.sum() / 60
    return data_pd


def count_df(df, keyword, interval=20):
    '''
    5月27日重构的最新版函数
    用于获取一段时间间隔内，某个数据点(VarName)的数量
    '''
    # 筛选出有关键字keyword(“节拍”)有关的数据
    df_keyword = df.loc[(df["VarName"].str.contains(keyword)), ["VarName", "AlarmType", "DateTime"]]
    df_keyword.set_index("DateTime", inplace=True, drop=False)
    resampled = df_keyword.resample(f"{interval}T")
    data_pd = resampled["VarName"].count()
    print(f"{keyword}的数据", data_pd)
    return data_pd


def get_pause(df):
    '''
    5月27日重构的最新版函数
    用于获取一段时间间隔内，某个急停点(StartVarName)的时间
    '''
    # df["Interval"] = df["Interval"]/60
    grouped = df.groupby(by="StartVarName")
    top5 = grouped["Interval"].sum().sort_values(ascending=False).head(5)
    return top5


if __name__ == '__main__':
    data = get_count(db=db, line="w7", point="商检房出口直通",
                     interval=60, workday=True)
    print("以下是输出数据")
    print(data)
